package com.lagou.filter;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * 功能描述
 *
 * @author wangqiang
 * @since 2021/09/27
 */
@Component
public class RegisterTimeFilter implements GlobalFilter, Ordered {
    private static final Logger logger = LoggerFactory.getLogger(RegisterTimeFilter.class);


    private int maxRegisterRequest = 5; // 时间范围内最大访问次数

    private long timeInterval = 60 * 1000;  // 时间范围

    private static final Map<String, RegisterTime> registerTimeMap = new HashMap<>();

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        ServerHttpResponse response = exchange.getResponse();
        String uri = request.getURI().getPath();
        if (!uri.contains("/user/register/")) {
            return chain.filter(exchange);
        }
        logger.info(">>>>>>RegisterTimeFilter 防IP爆刷过滤");
        String clientIp = request.getRemoteAddress().getHostString();
        if (isIpMoreRequest(clientIp)) {
            response.setStatusCode(HttpStatus.FORBIDDEN);
            String data = "请求过频繁！";
            DataBuffer wrap = response.bufferFactory().wrap(data.getBytes());
            return response.writeWith(Mono.just(wrap));
        }
        return chain.filter(exchange);
    }

    private boolean isIpMoreRequest(String clientIp) {
        RegisterTime reqCount = registerTimeMap.get(clientIp);
        if (reqCount == null) {
            RegisterTime registerTime = new RegisterTime(clientIp);
            registerTimeMap.put(clientIp, registerTime);
            return false;
        } else {
            logger.info(">>>ip:{}上次请求区间内请求了{}次", clientIp, reqCount.getRequestCount());
            // ip请求记录开始时间
            long startTime = reqCount.getStartTime();
            // 当前时间
            long currentTime = new Date().getTime();

            // 如果请求时间据上次请求超过设置时间区间，重置记录
            if (startTime + timeInterval < currentTime) {
                RegisterTime registerTime = new RegisterTime(clientIp);
                registerTimeMap.put(clientIp, registerTime);
                logger.info(">>>ip:{}本次请求区间内请求了{}次", clientIp, registerTime.getRequestCount());
                return false;
            } else {
                // 如果请求时间据上次请求不超过设置时间区间，校验是否超过次数
                if (reqCount.getRequestCount() > maxRegisterRequest) {
                    reqCount.addReqCount();
                    logger.info(">>>ip:{}本次请求区间内请求了{}次", clientIp, reqCount.getRequestCount());
                    return true;
                } else {
                    reqCount.addReqCount();
                    logger.info(">>>ip:{}本次请求区间内请求了{}次", clientIp, reqCount.getRequestCount());
                    return false;
                }
            }
        }

    }

    @Override
    public int getOrder() {
        return 0;
    }

    private class RegisterTime {
        private String clientIp;

        private long startTime;

        private int requestCount = 0;

        public RegisterTime() {
        }

        public RegisterTime(String clientIp) {
            this.clientIp = clientIp;
            this.startTime = new Date().getTime();
            this.requestCount = 1;
        }

        public String getClientIp() {
            return clientIp;
        }

        public long getStartTime() {
            return startTime;
        }

        public int getRequestCount() {
            return requestCount;
        }

        public void addReqCount() {
            this.requestCount += 1;
        }
    }
}